#include "server.h"

Server::Server(char* DEVICE)
{
    this->fc = new FlowControl(DEVICE);
    this->buffer = new List();
}

void Server::controlServer()
{
    message *msg;
    cout << "Servidor" << endl;
    cout << "..." << endl;
    int r;
    bool getOut = false;
    
    while ( !getOut){
        if ( (msg = this->fc->getRsDescriptor()->getMessage(&r)) ){
            switch (msg->getType()){
                case 'c':
                    this->fc->sendNack();
                    this->cCmd();
                    break;
                case 'f':
                    this->fc->sendNack();
                    this->fCmd();
                    break;
                case 'e':
                    this->fc->sendNack();
                    this->eCmd();
                    break;
                case 'l':
                    if (msg->getSize()-2 > 0){
                        this->fc->sendNack();
                        this->lCmd(1);
                    } else
                        this->lCmd(0);
                    break;
                case 'a':
                    this->fc->sendNack();
                    this->aCmd();
                    break;
                case 'q':
                    getOut = !getOut;
                    break;
                default:
                    //cerr << "Comando não encontrado " << msg->getType() << endl;
                    break;
            }
        }

        delete msg;
    }

    cout << "Finalizado." << endl;
}

void Server::lCmd(short int type)
{
    FILE *fp;
    char c;
    string lsCommand = "ls -l ";
    
    this->buffer->clear();

    if (type){    
        if (this->fc->receiveUntilZ(this->buffer, 'l', 0))
            lsCommand += (char *)this->buffer->getPointerData();
    }

    this->buffer->clear();
    fp = popen(lsCommand.c_str(), "r");

    for (c = fgetc(fp); c != EOF; c = fgetc(fp))
        this->buffer->insert(c);
        
   if ( !this->fc->sendUntilZ(this->buffer->getPointerData(), this->buffer->getSize(),
            (byte)'X', (byte)'Y'))
       cout << "Erro: não consegui enviar o resultado completo para o cliente - comando l" << endl;

    this->buffer->clear();
    pclose(fp);
}


void Server::cCmd()
{
    message *msg = new message();
    DIR *dir = opendir((const char *)this->buffer->getPointerData());
    int fd;
    char answerType, answerData;
        
    if (this->fc->receiveUntilZ(this->buffer, 'c', 0)){
        dir = opendir((const char *)this->buffer->getPointerData());

        if ( !dir){
            if (errno == EACCES) /* erro na permisao */
                answerData = S_ERROR_DENY_R;
            else if (errno == ENOTDIR) /* não é diretorio */
                answerData = S_ERROR_UNKN;
            else if (errno == ENOENT) /* diretorio nao existe */
                answerData = S_ERROR_DIR_N_EXIT;
            answerType = 'E';
        } else {
            fd = dirfd(dir);
            fchdir(fd);
            closedir(dir);
            answerType = 'Y';
        }
    } else
        cout << "Erro, não recebi solicitação completa 'c'" << endl;

    msg->setMessage(0, (byte)answerType, (byte *)&answerData, 1);
    this->fc->send(msg, NULL, 'Y');

    this->buffer->clear();
    delete msg;
}

void Server::fCmd()
{
    message *msg = new message();
    fileManagement *file;
    byte st;

    if (this->fc->receiveUntilZ(this->buffer, 'f', 0)){
        file = new fileManagement((char *)this->buffer->getPointerData(), MODE_R);
        st = file->currentStatus();
        
        if ((file->currentStatus() == S_READY || file->currentStatus() == S_EOF) &&
                            (size_t)file->currentDataSize() > 0){
            msg->setMessage(0, 'Y', NULL, 0);
            this->fc->send(msg, NULL, 'Y');
            this->fc->sendUntilZ(file, 'X', 'Y');
        } else {
            msg->setMessage(0, 'N', &st, 1);
            this->fc->send(msg, NULL, 'Y');
        }
    } else
        cout << "Erro, não recebi solicitação completa 'f'" << endl;

    delete msg;
    delete file;
    this->buffer->clear();
}

void Server::aCmd()
{
    message *msg = new message();
    fileManagement *file;
    byte st;
    bool out = false;

    if (this->fc->receiveUntilZ(this->buffer, 'a', 0)){
        file = new fileManagement((char *)this->buffer->getPointerData(), MODE_RW);
        st = file->currentStatus();
        
        if (file->currentStatus() == S_READY || file->currentStatus() == S_EOF){
            msg->setMessage(0, 'Y', NULL, 0);
            file->writeMode(APPEND);
            
            this->fc->send(msg, NULL, 'Y');

            /* Espera até 1 minuto por uma resposta do usuário */
            /* time out = 16 */
            /* 1minuto = 60s/16 ~ 4 */
            for (short i = 0; i < 4 && !out; i++){
                this->buffer->clear();
                
                if (this->fc->receiveUntilZ(this->buffer, 'D', 0)){
                    file->append(this->buffer->getPointerData(), this->buffer->getSize());
                    out = !out;
                }
            }

            if ( !out)
                cout << "Erro, não recebi solicitação completa 'a'" << endl;
        } else {
            msg->setMessage(0, 'N', &st, 1);
            this->fc->send(msg, NULL, 'Y');
        }
    } else
        cout << "Erro, não recebi solicitação completa 'a'" << endl;

    this->buffer->clear();
    delete file;
    delete msg;
}

void Server::eCmd()
{
    message *msg = new message();
    byte st;
    fileManagement *file;

    if (this->fc->receiveUntilZ(this->buffer, 'e', 0)){
        file  = new fileManagement((char *)this->buffer->getPointerData(), MODE_RW);
        st = file->currentStatus();

        if (file->currentStatus() == S_READY || file->currentStatus() == S_EOF){
            msg->setMessage(0, 'Y', NULL, 0);

            this->fc->send(msg, NULL, 'Y', false, false);
            this->eCmdFinal(file);
        } else {
            msg->setMessage(0, 'N', &st, 1);
            this->fc->send(msg, NULL, 'Y', false, false);
        }
    } else
        cout << "Erro, não recebi solicitação completa 'e'" << endl;
    
    delete msg;
    this->buffer->clear();
    delete file;
}

void Server::eCmdFinal(fileManagement *file)
{
    message *msg = new message();
    byte st;
    bool out = false;
    
    this->buffer->clear();
    
    if (this->fc->receiveUntilZ(this->buffer, 'g', 0)){
        if (file->setLine(atoi((const char*)this->buffer->getPointerData()))){
            msg->setMessage(0, 'Y', NULL, 0);

            this->fc->send(msg, NULL, 'Y');

            /* Espera até 1 minuto por uma resposta do usuário */
            /* time out = 16 */
            /* 1 minuto = 60s/16 ~ 4 */
            for (short i = 0; i < 4 && !out; i++){
                this->buffer->clear();

                if (this->fc->receiveUntilZ(this->buffer, 'D', 0)){
                    file->editLine(this->buffer->getPointerData(), this->buffer->getSize());
                    file->saveEdit();
                    out = !out;
                }
            }
        } else {
            st = S_ERROR_UNKN;
            msg->setMessage(0, 'N', &st, 1);
            this->fc->send(msg, NULL, 'Y');
        }
    } else
        cout << "Erro, não recebi solicitação completa 'g'" << endl;

    delete msg;
    this->buffer->clear();
}
